package binary;

public class Binary64 {
	
	//
	// Global constants required to implement methods
	//
	
	public static final long	TRUE		= (long) 0xFFFFFFFFFFFFFFFFL;
	public static final long	FALSE		= (long) 0x0000000000000000L;
	public static final long	FIRST		= (long) 0x8000000000000000L;
	public static final long	LAST		= (long) 0x0000000000000001L;
	public static final long	LEFT		= (long) 0xFFFFFFFF00000000L;
	public static final long	RIGHT		= (long) 0x00000000FFFFFFFFL;
	public static final int		NUMBITS		= 64;
	
	//
	// Methods for dealing with individual arrays
	//
	public static final long reverse(long mask) {
		int		i;
		long	V = FALSE;
		for(i=0; i<NUMBITS; i++) {
			V = setBitFromLeft(i, getBitFromRight(i, mask), V);
		}
		return V;
	}
	
	public static final long not(long mask) {
		return (long) (TRUE ^ mask);
	}
	
	public static final boolean	getBitFromLeft(int bit, long mask) {
		return getBitFromRight(NUMBITS-1-bit, mask);
	}
	
	public static final boolean getBitFromRight(int bit, long mask) {
		return ( (LAST << bit) & mask) != 0;
	}
	
	public static final long setBitFromLeft(int bit, boolean value, long mask) {
		return setBitFromRight(NUMBITS-1-bit, value, mask);
	}
	
	public static final long setBitFromRight(int bit, boolean value, long mask) {
		if (value==true) {
			return (long) ((LAST << bit) | mask);
		} else {
			return (long) (not((long) (LAST << bit)) & mask);
		}
	}
	
	public static final long getAllFromLeft(int offset, long mask) {
		int		i;
		long	V = FALSE;
		for(i=0; i<offset; i++) {
			V = setBitFromLeft( i, getBitFromLeft(i, mask), V);
		}
		return V;
	}
	
	public static final long getAllFromRight(int offset, long mask) {
		int		i;
		long	V = FALSE;
		for(i=0; i<offset; i++) {
			V = setBitFromRight( i, getBitFromRight(i, mask), V);
		}
		return V;
	}
	
	public static final long setAllFromLeft(int offset, boolean value, long mask) {
		int		i;
		for(i=0; i<offset; i++) {
			mask = setBitFromLeft( i, value, mask);
		}
		return mask;
	}
	
	public static final long setAllFromRight(int offset, boolean value, long mask) {
		int		i;
		for(i=0; i<offset; i++) {
			mask = setBitFromRight( i, value, mask);
		}
		return mask;
	}
	
	public static final long getBitsLeftToRight(int start, int range, long mask) {
		return getBitsRightToLeft(NUMBITS-(start+range), range, mask);
	}
	
	public static final long getBitsRightToLeft(int start, int range, long mask) {
		int		i;
		long	V = FALSE;
		for(i=start; i<range+start; i++) {
			V = setBitFromRight(i, getBitFromRight(i, mask), V);
		}
		return V;
	}
	
	
	//
	// Methods for dealing with arrays
	//
	
	public static final long[] copy(long B[]) {
		long	A[] = new long[B.length];
		int		i;
		for(i=0; i<B.length; i++) {
			A[i] = B[i];
		}
		return A;
	}
	
	public static final void reverse(long B[]) {
		int		i;
		long	tmp;
		
		for(i=0; i<B.length-i; i++) {
			tmp = B[i];
			B[i] = B[B.length-i-1];
			B[B.length-i-1] = tmp;
		}
	}
	
	public static final void reverseBits(long B[]) {
		int	i;
		reverse(B);
		for(i=0; i<B.length; i++) {
			B[i] = reverse(B[i]);
		}
	}
	
	public static final boolean	getBitFromLeft(int bit, long B[]) {
		return getBitFromLeft(bit%NUMBITS, B[bit/NUMBITS]);
	}
	
	public static final boolean	getBitFromRight(int bit, long B[]) {
		return getBitFromRight(bit%NUMBITS, B[B.length - 1 - bit/NUMBITS]);
	}
	
	public static final void setBitFromLeft(int bit, boolean value, long B[]) {
		B[bit/NUMBITS] = setBitFromLeft( bit % NUMBITS, value, B[bit / NUMBITS] );
	}

	public static final void setBitFromRight(int bit, boolean value, long B[]) {
		B[B.length - 1 - bit/NUMBITS] = setBitFromRight( bit%NUMBITS, value, B[B.length - 1 - bit/NUMBITS] );
	}
	
	public static final void setAll(long value, long B[]) {
		int i;
		for(i=0; i<B.length; i++) {
			B[i] = value;
		}
	}
	
	public static final void setAllBits(boolean value, long B[]) {
		if (value==true) {
			setAll(TRUE, B);
		} else {
			setAll(FALSE, B);
		}
	}
	
	public static final long[] and(long A[], long B[]) {
		long	C[] = new long[A.length];
		int		i;
		for(i=0; i<A.length; i++) {
			C[i] = (long) (A[i] & B[i]);
		}
		return C;
	}

	public static final long[] or(long A[], long B[]) {
		long	C[] = new long[A.length];
		int		i;
		for(i=0; i<A.length; i++) {
			C[i] = (long) (A[i] | B[i]);
		}
		return C;
	}
	
	public static final long[] xor(long A[], long B[]) {
		long	C[] = new long[A.length];
		int		i;
		for(i=0; i<A.length; i++) {
			C[i] = (long) (A[i] ^ B[i]);
		}
		return C;
	}
	
	public static final long[] not(long A[]) {
		long	C[] = new long[A.length];
		int		i;
		for(i=0; i<A.length; i++) {
			C[i] = (long) not(A[i]);
		}
		return C;
	}
	
	public static final long[] shiftRight(int numBits, long A[]) {
		int		i;
		int		bitLen = A.length * NUMBITS;
		long	B[] = Binary64.copy(A);

		if (numBits > 0) {
			for(i=0; i<bitLen-numBits; i++) {
				Binary64.setBitFromRight(i,  Binary64.getBitFromRight(i + numBits, A), B);
			}
			for(i=bitLen-numBits; i<bitLen; i++) {
				Binary64.setBitFromRight(i,  false, B);
			}
		}
		return B;
	}
	public static final long[] shiftLeft(int numBits, long A[]) {
		int		i;
		int		bitLen = A.length * NUMBITS;
		long	B[] = Binary64.copy(A);

		if (numBits > 0) {
			for(i=0; i<bitLen-numBits; i++) {
				Binary64.setBitFromLeft(i,  Binary64.getBitFromLeft(i + numBits, A), B);
			}
			for(i=bitLen-numBits; i<bitLen; i++) {
				Binary64.setBitFromLeft(i,  false, B);
			}
		}
		return B;
	}
	
	//
	// Methods for printing a long in different formats 
	//
	
	public static final String toBinaryString(long b) {
		String	S = "";
		int		i;
		
		for(i=0; i<NUMBITS; i++) {
			if (getBitFromLeft(i, b)) {
				S += "1";
			} else {
				S += "0";
			}
		}
		return S;
	}
	
	public static final String toHexString(long b) {
		String	S = "";
		String	H = "";
		int		i;
		
		for(i=0; i<NUMBITS; i++) {
			if (getBitFromLeft(i, b)) {
				S += "1";
			} else {
				S += "0";
			}
			if (S.length()==4) {
				switch(S) {
					case	"0000"	:	H +=  "0";	break;
					case	"0001"	:	H +=  "1";	break;
					case	"0010"	:	H +=  "2";	break;
					case	"0011"	:	H +=  "3";	break;
					case	"0100"	:	H +=  "4";	break;
					case	"0101"	:	H +=  "5";	break;
					case	"0110"	:	H +=  "6";	break;
					case	"0111"	:	H +=  "7";	break;
					case	"1000"	:	H +=  "8";	break;
					case	"1001"	:	H +=  "9";	break;
					case	"1010"	:	H +=  "A";	break;
					case	"1011"	:	H +=  "B";	break;
					case	"1100"	:	H +=  "C";	break;
					case	"1101"	:	H +=  "D";	break;
					case	"1110"	:	H +=  "E";	break;
					case	"1111"	:	H +=  "F";	break;
				}
				S = "";
			}
		}
		
		if (S.length() % 4 != 0) {
			while(S.length()%3!=0) {
				S = "0" + S;
			}
			switch(S) {
				case	"0000"	:	H +=  "0";	break;
				case	"0001"	:	H +=  "1";	break;
				case	"0010"	:	H +=  "2";	break;
				case	"0011"	:	H +=  "3";	break;
				case	"0100"	:	H +=  "4";	break;
				case	"0101"	:	H +=  "5";	break;
				case	"0110"	:	H +=  "6";	break;
				case	"0111"	:	H +=  "7";	break;
				case	"1000"	:	H +=  "8";	break;
				case	"1001"	:	H +=  "9";	break;
				case	"1010"	:	H +=  "A";	break;
				case	"1011"	:	H +=  "B";	break;
				case	"1100"	:	H +=  "C";	break;
				case	"1101"	:	H +=  "D";	break;
				case	"1110"	:	H +=  "E";	break;
				case	"1111"	:	H +=  "F";	break;
			}
		}
		return H;
	}
	
	public static final String toQuadString(long b) {
		String	S = "";
		String	O = "";
		int		i;
		
		for(i=0; i<NUMBITS; i++) {
			if (getBitFromLeft(i, b)) {
				S += "1";
			} else {
				S += "0";
			}
			if (S.length()==2) {
				switch(S) {
					case	"00"	:	O +=  "0";	break;
					case	"01"	:	O +=  "1";	break;
					case	"10"	:	O +=  "2";	break;
					case	"11"	:	O +=  "3";	break;
				}
				S = "";
			}
		}
		
		if (S.length() % 2 != 0) {
			while(S.length()%2!=0) {
				S = "0" + S;
			}
			switch(S) {
				case	"000"	:	O =  "0" + O;	break;
				case	"001"	:	O =  "1" + O;	break;
				case	"010"	:	O =  "2" + O;	break;
				case	"011"	:	O =  "3" + O;	break;
			}
		}
		return O;
	}
	
	public static final String toOctalString(long b) {
		String	S = "";
		String	O = "";
		int		i;
		
		for(i=0; i<NUMBITS; i++) {
			if (getBitFromRight(i, b)) {
				S = "1" + S;
			} else {
				S = "0" + S;
			}
			if (S.length()==3) {
				switch(S) {
					case	"000"	:	O =  "0" + O;	break;
					case	"001"	:	O =  "1" + O;	break;
					case	"010"	:	O =  "2" + O;	break;
					case	"011"	:	O =  "3" + O;	break;
					case	"100"	:	O =  "4" + O;	break;
					case	"101"	:	O =  "5" + O;	break;
					case	"110"	:	O =  "6" + O;	break;
					case	"111"	:	O =  "7" + O;	break;
				}
				S = "";
			}
		}
		
		if (S.length() % 3 != 0) {
			while(S.length()%3!=0) {
				S = "0" + S;
			}
			switch(S) {
				case	"000"	:	O =  "0" + O;	break;
				case	"001"	:	O =  "1" + O;	break;
				case	"010"	:	O =  "2" + O;	break;
				case	"011"	:	O =  "3" + O;	break;
				case	"100"	:	O =  "4" + O;	break;
				case	"101"	:	O =  "5" + O;	break;
				case	"110"	:	O =  "6" + O;	break;
				case	"111"	:	O =  "7" + O;	break;
			}
		}
		return O;
	}
		
	//
	// Methods for printing arrays of in different formats 
	//
	
	public static final String toBinaryString(long B[]) {
		String	S = "\n";
		int		i;
		for(i=0; i<B.length; i++) {
			S = S + toBinaryString(B[i]) + "\n";
		}
		return S;
	}
	
	public static final String toHexString(long B[]) {
		String	S = "\n";
		int		i;
		for(i=0; i<B.length; i++) {
			S = S + toHexString(B[i]) + "\n";
		}
		return S;
	}
	
	public static final String toOctalString(long B[]) {
		String	S = "\n";
		int		i;
		for(i=0; i<B.length; i++) {
			S = S + toOctalString(B[i]) + "\n";
		}
		return S;
	}
	
	//
	// Methods to convert into the appropriate array format
	//
	public static final	long[] toArray(short A[]) {
		long	B[] = new long[A.length*2];
		int		i;
		for(i=0; i<A.length; i++) {
			B[i*2]		= Binary16.getLeftSide(A[i]);
			B[i*2+1]	= Binary16.getRightSide(A[i]);
		}
		return B;
	}
	
	public static final long[] toArray(int A[]) {
		return toArray(Binary16.toArray(A));
	}
	
	public static final long[] toArray(long A[]) {
		return toArray(Binary16.toArray(A));
	}
	
	//
	// Getting and setting each side
	//
	public static final int getLeftSide(long mask) {
		return (int) ((mask & LEFT) >>> (NUMBITS/2));
	}
	
	public static final int getRightSide(long mask) {
		return (int) (mask & RIGHT);
	}
	
	public static final long setLeftSide(int value, long mask) {
		return (long) ((mask & RIGHT) | (value << NUMBITS/2));
	}
	
	public static final long setRightSide(int value, long mask) {
		return (long) ((mask & LEFT) | value);
	}
	
	
}

